﻿// Keras-Sharp: C# port of the Keras library
// https://github.com/cesarsouza/keras-sharp
//
// Based under the Keras library for Python. See LICENSE text for more details.
//
//    The MIT License(MIT)
//    
//    Permission is hereby granted, free of charge, to any person obtaining a copy
//    of this software and associated documentation files (the "Software"), to deal
//    in the Software without restriction, including without limitation the rights
//    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//    copies of the Software, and to permit persons to whom the Software is
//    furnished to do so, subject to the following conditions:
//    
//    The above copyright notice and this permission notice shall be included in all
//    copies or substantial portions of the Software.
//    
//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//    SOFTWARE.
//

namespace KerasSharp
{
    using Accord.Math;
    using KerasSharp.Engine.Topology;
    using KerasSharp.Layers;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;


    public static partial class Python
    {
        /// <summary>
        ///   Input() is used to instantiate a Keras tensor.
        /// </summary>
        /// 
        /// <remarks>
        ///   A Keras tensor is a tensor object from the underlying backend (Theano or TensorFlow), which we 
        ///   augment with certain attributes that allow us to build a Keras model just by knowing the inputs
        ///   and outputs of the model.
        /// </remarks>
        /// 
        /// <param name="shape">A shape tuple (integer), including the batch size. For instance, 
        ///   <c>batch_shape= (10, 32)</c> indicates that the expected input will be batches of 10 32-dimensional
        ///   vectors. <c>batch_shape= (None, 32)</c> indicates batches of an arbitrary number of 32-dimensional 
        ///   vectors.</param>
        /// <param name="batch_shape">The batch shape.</param>
        /// <param name="name">An optional name string for the layer. Should be unique in a model (do not reuse
        ///   the same name twice). It will be autogenerated if it isn't provided.</param>
        /// <param name="dtype">The data type expected by the input, as a string
        ///   (`float32`, `float64`, `int32`...)</param>
        /// <param name="sparse">A boolean specifying whether the placeholder to be created is sparse.</param>
        /// <param name="tensor">Optional existing tensor to wrap into the `Input` layer.
        ///   If set, the layer will not create a placeholder tensor.</param>
        ///   
        public static List<Tensor> Input(int?[] shape = null, int?[] batch_shape = null, string name = null,
            DataType? dtype = null, bool sparse = false, Tensor tensor = null)
        {
            // https://github.com/fchollet/keras/blob/f65a56fb65062c8d14d215c9f4b1015b97cc5bf3/keras/engine/topology.py#L1416

            if (batch_shape == null && tensor == null)
            {
                throw new ArgumentException("Please provide to Input either a 'shape' or a 'batch_shape' argument. Note that " +
                    "'shape' does not include the batch dimension.");
            }

            if (shape != null && batch_shape != null)
                batch_shape = new int?[] { null }.Concatenate(shape);

            var input_layer = new InputLayer(batch_input_shape: batch_shape,
                                     name: name, dtype: dtype,
                                     sparse: sparse,
                                     input_tensor: tensor);

            // Return tensor including _keras_shape and _keras_history.
            // Note that in this case train_output and test_output are the same pointer.
            return input_layer.inbound_nodes[0].output_tensors;
        }
    }
}
